/* 
 * 	This file is part of VPN Monitor Project 7..

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package monitor;

import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Timer;

import monitor.gui.GuiControl;
import monitor.interfaces.observers.TrafficObserver;
import monitor.interfaces.observers.TrafficStringObserver;
import monitor.interfaces.observers.TrafficBufferObserver;
import monitor.traffic.Worker;

public class Monitor {
	private Watcher watch = null;
	private Worker worker = null;
	private Timer timer = null;
	private GuiControl guiControl = null;
	private boolean running = false;
	private LinkedList<TrafficBufferObserver> tbo = null;
	private LinkedList<TrafficStringObserver> tso = null;
	private LinkedList<TrafficObserver> to = null;
	private ApplicationMonitor appMonitor = null;
	public final static int TRAFFIC_REPORT_LEVEL = 2;
	public final static String APPLICATION_NAME = "VPN Monitor Project 7.";
	
	private Monitor(){
		tbo = new LinkedList<TrafficBufferObserver>();
		tso = new LinkedList<TrafficStringObserver>();
		to = new LinkedList<TrafficObserver>();
		guiControl = new GuiControl(this);
	}
	
	public void halt(String source) {
		stop(source);
		running = false;
		timer = null;
	}
	
	private void stop(String source){
		if(running){
			if(timer!=null)
				timer.cancel();
			if(worker!=null)
				worker.halt();
			if(appMonitor!=null)
				appMonitor.terminateAll();
			guiControl.halt(source);
		}
	}
	
	public void exit(String source){
		if(watch!=null)
			stop(source);
		System.exit(0);
	}
	
	public void addLogEntry(String entry){
		guiControl.addLogEntry(entry);
	}
	
	public void changeActiveLimits(HashMap<String, Boolean> limitActive){
		worker.changeActiveLimits(limitActive);
	}
	
	public void changeLimits(HashMap<String, Long> limitValue){
		worker.changeLimits(limitValue);
	}
	
	public void changeLogLevel(HashMap<String, Long> logLevel){
		worker.changeLogLevel(logLevel);
	}
	
	public void addRemoveApplication(String[] apps){
		if(apps!=null)
			appMonitor.addRemoveApplication(apps);
		else
			appMonitor.terminateAll();
	}
	
	public void changeTimers(HashMap<String, Long> timerValue){
		final String[] TIMERS = {"TIMER_MONITOR", "TIMER_TRAFFIC"};
		if(timerValue.containsKey(TIMERS[0]))
			changeWatchTimer(timerValue.get(TIMERS[0]));
		if(timerValue.containsKey(TIMERS[1]))
			worker.changeDelay(timerValue.get(TIMERS[1]));
	}
	
	public synchronized void addTrafficUtilityObserver(TrafficBufferObserver ob){
		tbo.add(ob);
		if(running)
			worker.addTrafficBufferObserver(ob);
	}
	
	public synchronized void addTrafficStringObserver(TrafficStringObserver ob){
		tso.add(ob);
		if(running)
			worker.addTrafficStringObserver(ob);
	}
	
	public synchronized void addTrafficObserver(TrafficObserver ob){
		to.add(ob);
		if(running)
			worker.addTrafficObserver(ob);
	}
	
	public void changeSample(int sample){
		worker.changeSample(sample);
	}
	
	public boolean isRunning(){
		return running;
	}
	
	public void start(String[] network,
			HashMap<String, Boolean> limitActive,
			HashMap<String, Long> limitValue,
			HashMap<String, Long> logValue,
			HashMap<String, Long> timerValue, int sampleSize, String[] application) {
			
		addLogEntry("Monitor started");
		running = true;

		if(watch==null){
			worker = new Worker(this, network[1], sampleSize, timerValue.get("TIMER_TRAFFIC"), limitActive, limitValue, logValue);
			watch = new Watcher(this, network[0]);			
		} else {
			worker.changeNetwork(network[1], sampleSize, timerValue.get("TIMER_TRAFFIC"));
			watch.changeInterfaceName(network[0]);
			watch = new Watcher(watch);
		}
		
		if(appMonitor==null)
			appMonitor = new ApplicationMonitor();
		if(application == null)
			appMonitor.terminateAll();
		else
			appMonitor.addRemoveApplication(application);
		
		changeWatchTimer(timerValue.get("TIMER_MONITOR"));
		for(TrafficBufferObserver tbo : this.tbo)
			worker.addTrafficBufferObserver(tbo);
		for(TrafficObserver to : this.to)
			worker.addTrafficObserver(to);
		for(TrafficStringObserver tso : this.tso)
			worker.addTrafficStringObserver(tso);
	}
	
	private void changeWatchTimer(long delay){
		if(timer!=null)
			timer.cancel();
		timer = new Timer();
		timer.scheduleAtFixedRate(watch, delay, delay);
	}
	
	public static void main(String[] args){
		new Monitor();
	}
	
	private class ApplicationMonitor{
		private HashMap<String, Process> processes = null;
		
		private ApplicationMonitor(){
			processes = new HashMap<String, Process>();
		}
				
		private void addRemoveApplication(String[] apps){
			LinkedList<String> terminate = new LinkedList<String>(processes.keySet());
			for(String s : apps){
				if(!processes.containsKey(s))
					processes.put(s, startProcess(s));
				else
					terminate.remove(s);
			}
			for(String s : terminate)
				processes.get(s).destroy();
		}
		
		private void terminateAll(){
			for(Process p : processes.values())
				p.destroy();
			processes.clear();
		}
		
		private Process startProcess(String application){
			try {
				return new ProcessBuilder(application).start();
			} catch (IOException e) {
				e.printStackTrace();
			}
			return null;
		}
	}
}
